#include <stdio.h>
#include <stdlib.h>
// Ok, not support windows platform
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

enum direction {
  direction_fwd,
  direction_bwd
};

char *find_match(char *src, char match, enum direction direct) {
  char *aux = src;// aux is helper, like tmp
  int count = 1;
  while(count) {
    direct == direction_fwd ? ++aux : --aux;
    if (*aux == *src) ++count;
    if (*aux == match) --count;
  }

  return aux;
}

void prepare_jumps(char *program, ssize_t prog_size, char *jump_map[]) {
  char *aux = program;

  while (aux < (program + prog_size)) {
    switch (*aux) {
      case '[':
        jump_map[aux-program] = find_match(aux, ']', direction_fwd);
        break;
      case ']':
        jump_map[aux-program] = find_match(aux, '[', direction_bwd);
        break;
    }
    ++aux;
  }
}

ssize_t read_prog(char *buf, ssize_t buf_size, int fd) {
  char *aux_ptr = buf;
  ssize_t prog_size = 0;
  ssize_t aux_size = buf_size;

  ssize_t ret = read(fd, aux_ptr, aux_size);

  while (ret && (prog_size < buf_size)) {
    aux_size -= ret;
    prog_size += ret;
    aux_ptr += ret;
    ret = read(fd, aux_ptr, aux_size);
  }

  return prog_size;
}

int main(int argc, char *argv[])
{
  char program[50000];// why, and whu not define
  char memory[30000];
  char *jump_map[50000];
  char *ip = program;
  char *ptr = memory;
  int fd = 0;
  ssize_t prog_size = 0;

  if (argc < 2) {
    fd = STDIN_FILENO;
  } else {
    fd = open(argv[1], O_RDONLY);

    if(fd < 0){
      fprintf(stderr, "Error opening %s!\n", argv[1]);
      perror("open");
      exit(1);
    }

  }
  prog_size = read_prog(program, sizeof(program), fd);

  prepare_jumps(program, prog_size, jump_map);

  while (ip < (program + prog_size)) {
    switch(*ip) {
      case '>': ++ptr; break;
      case '<': --ptr; break;
      case '+': ++(*ptr); break;
      case '-': --(*ptr); break;
      case '.': putchar(*ptr); 
        fflush(stdout); 
        break;
      case ',': *ptr = getchar(); 
        if (*ptr == EOF) exit(0);
        break;
      case '[':
        if (!*ptr) {
          ip = jump_map[ip-program];
        }
        break;
      case ']':
        if (*ptr) {
          ip = jump_map[ip-program];
        }
        break;
    }
    ++ip;
  }

  return 0;
}
